package mathy.wili.c.asm;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import mathy.c.Ca;
import mathy.wili.autoeq.Refs;
import mathy.wili.c.Class9;
import mathy.wili.c.MiscFilter.ClsFilter;
import mathy.wili.c.asm.AsmItems.AsmItem;
import mathy.wili.extract_srcFile.java.FieldRange;
import net.bytebuddy.jar.asm.ClassReader;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.jar.asm.FieldVisitor;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.jar.asm.Type;
/**
 * Find in class: 在类中访问：内部类 | 属性 | 方法
 * @author weila 2021年3月2日
 */
public class ClsVisitor extends ClassVisitor {
	static final int UseLog = -Asm9.UseLog;

	final Class<?> clazz;

	final ClsFilter filt;

	final AsmItems toList;

	final AsmCont cont;

	Executable method;

	/**
	 * 1静态元素 | 2对象元素 | 3静态或非静态元素
	 */
	int initOpt = -1;
	public ClsVisitor(ClassVisitor classVisitor, Class<?> cls, ClsFilter filt, AsmCont cont) {
		super(Asm9.AsmVerion, classVisitor);
		this.clazz = cls;
		this.filt = filt;
		this.toList = cont.toList;
		this.cont = cont;
	}

	/**
	 *  Used to find XX in init code. 
	 */
	public ClsVisitor setInitOpt(int initOpt) {
		Ca.asert(method == null, method);
		this.initOpt = initOpt;
		return this;
	}

	/**
	 *  Used to find given $method|$constructor 
	 */
	public ClsVisitor setMethod(Executable method) {
		Ca.asert(method == null || initOpt == -1, method);
		this.method = method;
		return this;
	}
	public String ownerCls;
	@Override
	public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
		ownerCls = name;
		if (cv != null) {
			cv.visit(version, access, name, signature, superName, interfaces);
		}
	}

	/**
	 * 	内部类
	 */
	public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) {
		if (initOpt < 0)
			return;
		//暂定：访问初始块的时候，访问内部类
		if ("456".isEmpty())
			return;
		String innerClassName = name.replace('/', '.');
		Class<?> inCls = Class9.forName(innerClassName, -1);
		toList.adde(-1, inCls);
		String[] nn = innerClassName.split("\\$");
		if (nn.length > 1) {
			Ca.pause();
		}
		ClsVisitor_findFun inVis = new ClsVisitor_findFun(null, filt, cont);
		ClassReader classReader = Asm9.newClassReader(innerClassName);
		if (classReader == null) {
			return;
		} else {
			classReader.accept(inVis, Opcodes.ASM5);
		}
	}

	/**
	 * 	属性
	 */
	@Override
	public FieldVisitor visitField(int access, String fieldName, String descriptor, String signature, Object value) {
		if (initOpt < 0)
			return null;
		if (method == null) {
			//当前仅用来收集属性的名义类型。
			FieldVisitor ret = super.visitField(access, fieldName, descriptor, signature, value);
			Ca.debug(UseLog, "C,visitField: ", fieldName);
			Field fd = Class9.getDeclaredField(fieldName, clazz);
			if (Modifier.isFinal(fd.getModifiers()))
				return ret;//常量必需赋值，在此建议不主动访问。
			FieldRange frange = FieldRange.getInstBy(fd);
			int line = -1;
			if (frange != null)
				line = frange.aLineNo;
			AsmItem item = toList.adde(line, fd);
			item.isFieldDefine = true;
			item.fieldRange = frange;
		}
		return null;
	}

	/**
	 * 	方法
	 */
	@Override
	public net.bytebuddy.jar.asm.MethodVisitor visitMethod(int access, String mName, String descriptor,
			String signature, String[] exceptions) {
		//包括对构造函数的访问（字段初始化时，访问的是空构造函数？）
		if (method != null) {
			if (method instanceof Constructor<?> && mName.equals(Asm9.INIT)) {
			} else if (!mName.equals(method.getName())) {
				return null;
			}
		}
		if (descriptor.contains("findPutField"))
			Ca.pause();
		Type[] types = net.bytebuddy.jar.asm.Type.getArgumentTypes(descriptor);
		Class<?>[] cc = Asm9.classesOfAsmTypes(types);
		Ca.debug(UseLog, inc, "==============C,visitMethod:", mName, ", pmTypes:", Arrays.asList(cc), ", line:");
		if (mName.equals("getValue"))
			Ca.pause();
		{
			String[] names = ownerCls.split("$");
			if (names.length > 1)
				Ca.pause();
		}
		if (initOpt < 0) {
			if (mName.equals(Asm9.CLINIT))//mName.equals(INIT)
				return null;
			//访问构造函数或其它函数
			Executable method2 = Asm9.methodOf(clazz, mName, cc);
			if (method == null) {
				if (method2 != null) {
					toList.adde(-123, method2);
				} else {
					if (Refs.ref())
						return null;
					Ca.asert(false, mName);
				}
			} else if (!method.equals(method2)) {
				return null;
			}
			if (method == null) {//访问所有的方法内容时，把当前方法也加入。
				toList.adde(-123, method2);
			}
		} else {
			if (initOpt == 3) {
				if (!mName.equals(Asm9.CLINIT) && !mName.equals(Asm9.INIT))
					return null;
			} else if (initOpt == 1) {
				if (!mName.equals(Asm9.CLINIT))
					return null;
			} else if (initOpt == 2) {
				if (!mName.equals(Asm9.INIT))
					return null;
			} else {
				Ca.asert(false, initOpt);
			}
		}
		MethodVisitor ret = super.visitMethod(access, mName, descriptor, signature, exceptions);
		MethodVisitor ret2 = new FunVisitor(ret, filt, cont);
		return ret2;
	}
	static int inc;
}
